package com.xc.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xc.core.bean.QueryBean;
import com.xc.core.enums.RedisPrefix;
import com.xc.core.enums.RedisTime;
import com.xc.core.utils.RedisUtils;
import com.xc.core.utils.ServiceUtils;
import com.xc.file.bean.CdnBean;
import com.xc.file.bean.FileBean;
import com.xc.file.bean.UpdateFileBean;
import com.xc.file.config.Constants;
import com.xc.file.dto.CdnDto;
import com.xc.file.dto.FileDto;
import com.xc.file.entity.*;
import com.xc.file.enums.FailCode;
import com.xc.file.mapper.*;
import com.xc.file.model.DownloadModel;
import com.xc.file.service.FileService;
import com.xc.file.service.FolderService;
import com.xc.file.service.UserInfoService;
import com.xc.tool.utils.DataUtils;
import com.xc.tool.utils.FileUtils;
import com.xc.tool.utils.ObjectUtils;
import com.xc.tool.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * <p>文件管理实现</p>
 *
 * @version v1.0
 */
@Service
@Slf4j
public class FileServiceImpl extends ServiceImpl<FileMapper, FileEntity> implements FileService {

    @Autowired
    private FolderService folderService;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private Constants constants;
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private SafetyChainMapper safetyChainMapper;
    @Autowired
    private ShareMapper shareMapper;
    @Autowired
    private FileHashMapper fileHashMapper;
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Override
    @Transactional
    public FileDto createFile(String userId, FileBean fileBean, boolean isVerify) {
        if (isVerify) {
            //验证目录信息
            if (!constants.getRootFolder().equals(fileBean.getFolderId())) {
                fileBean.setFolderId(folderService.verifyFolder(fileBean.getFolderId(), userId));
            }
            //计算当前用户使用空间
            userInfoService.verifyUserSpace(userId, fileBean.getSize());
        }
        //查询文件信息,检测文件是否重复
        FileEntity fileEntity = new FileEntity();
        fileEntity.setName(fileBean.getName());
        fileEntity.setUserId(userId);
        fileEntity.setFolderId(fileBean.getFolderId());
        FileEntity entity = fileMapper.selectOne(new QueryWrapper<>(fileEntity));
        fileEntity.setHashCode(fileBean.getHashCode());
        fileEntity.setSize(fileBean.getSize());
        //判断文件是否添加
        if (entity != null) {
            //文件已经创建，重命名创建文件
            String name = FileUtils.rename(entity.getName());
            fileEntity.setName(name);
        }
        if (!this.retBool(fileMapper.insert(fileEntity))) {
            throw FailCode.CREATE_FILE_FAIL.getOperateException();
        }
        //返回结果
        return ObjectUtils.convert(new FileDto(), fileEntity);
    }

    @Override
    public FileDto updateUserFile(String userId, String fileId, UpdateFileBean updateFileBean) {
        FileEntity fileEntity = getById(fileId);
        if (!userId.equals(fileEntity.getUserId())) {
            throw FailCode.NOT_OPERATE_AUTHORITY.getOperateException();
        }
        fileEntity.setName(updateFileBean.getName());
        if (!this.updateById(fileEntity)) {
            throw FailCode.UPDATE_FILE_FAIL.getOperateException();
        }
        return ObjectUtils.convert(new FileDto(), fileEntity);
    }

    @Override
    public List<FileDto> getUserFileList(String userId, QueryBean queryBean, FileBean fileBean) {
        FileEntity fileEntity = ObjectUtils.convert(new FileEntity(), fileBean);
        fileEntity.setUserId(userId);
        QueryWrapper<FileEntity> queryWrapper = ServiceUtils.queryData(queryBean, fileEntity);
        List<FileEntity> files = fileMapper.selectList(queryWrapper);
        List<FileDto> dtoList = ObjectUtils.convertList(files, FileDto.class);
        String downloadCode = StringUtils.generateOnlyId(constants.getMachineId());
        RedisUtils.set(RedisPrefix.USER_DOWNLOAD.getKey() + downloadCode, dtoList, RedisTime.USER_FILE.getTime());
        return setDownloadUrl(dtoList, "/user/" + downloadCode + "/file/");
    }

    @Override
    @Transactional
    public FileEntity deleteUserFile(String userId, String fileId) {
        //获取文件信息
        FileEntity fileEntity = fileMapper.selectById(fileId);
        //验证文件是否存在
        if (fileEntity == null) {
            throw FailCode.FILE_NOT_EXIST.getOperateException();
        }
        //验证是否有操作权限
        if (!fileEntity.getUserId().equals(userId)) {
            throw FailCode.NOT_OPERATE_AUTHORITY.getOperateException();
        }
        //删除文件信息
        if (!this.retBool(fileMapper.deleteById(fileId))) {
            throw FailCode.DELETE_FILE_FAIL.getOperateException();
        }
        return fileEntity;
    }

    @Override
    @Transactional
    public List<FileDto> updateUserFileList(String userId, String folderId, List<String> ids) {
        List<FileEntity> fileEntityList = new ArrayList<>();
        for (String id : ids) {
            FileEntity fileEntity = fileMapper.selectById(id);
            if (!fileEntity.getUserId().equals(userId)) {
                throw FailCode.NOT_OPERATE_AUTHORITY.getOperateException();
            }
            FileEntity entity = new FileEntity();
            entity.setFolderId(folderId);
            entity.setUserId(userId);
            entity.setName(fileEntity.getName());
            entity = fileMapper.selectOne(new QueryWrapper<>(entity));
            if (entity != null) {
                throw FailCode.FILE_EXIST.getOperateException();
            }
            fileEntity.setCreateTime(null);
            fileEntity.setFolderId(folderId);
            if (!this.updateById(fileEntity)) {
                throw FailCode.UPDATE_FILE_FAIL.getOperateException();
            }
            fileEntityList.add(fileEntity);
        }
        return ObjectUtils.convertList(fileEntityList, FileDto.class);
    }

    @Override
    public List<FileEntity> getFilesByFolderId(String folderId) {
        FileEntity fileEntity = new FileEntity();
        fileEntity.setFolderId(folderId);
        return fileMapper.selectList(new QueryWrapper<>(fileEntity));
    }

    @Override
    @Transactional
    public List<FileDto> createFiles(List<FileEntity> entities, boolean isRename) {
        List<FileEntity> fileEntityList = new ArrayList<>();
        for (FileEntity fileEntity : entities) {
            // 去重
            FileEntity entity = new FileEntity();
            entity.setFolderId(fileEntity.getFolderId());
            entity.setUserId(fileEntity.getUserId());
            entity.setName(fileEntity.getName());
            entity = fileMapper.selectOne(new QueryWrapper<>(entity));
            if (entity != null) {
                if (isRename) {
                    String name = FileUtils.rename(fileEntity.getName());
                    fileEntity.setName(name);
                } else {
                    throw FailCode.FILE_EXIST.getOperateException();
                }
            }
            fileEntity.setId(null);
            fileEntity.setCreateTime(null);
            fileEntity.setUpdateTime(null);
            fileEntity.setVersion(null);
            fileEntity.setUserId(fileEntity.getUserId());
            fileEntity.setFolderId(fileEntity.getFolderId());
            if (!this.save(fileEntity)) {
                throw FailCode.CREATE_FILE_FAIL.getOperateException();
            }
            fileEntityList.add(fileEntity);
        }
        return ObjectUtils.convertList(fileEntityList, FileDto.class);
    }

    @Override
    public List<FileDto> batchCopyFile(String userId, String folderId, List<String> fileIds, boolean isCopy) {
        List<FileEntity> fileArrayList = new ArrayList<>();
        if (fileIds.size() > 0) {
            // 获取待复制的文件
            QueryWrapper<FileEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.in("id", fileIds);
            List<FileEntity> fileList = fileMapper.selectList(queryWrapper);
            // 验证文件是不是用户的
            if (isCopy) {
                for (FileEntity fileEntity : fileList) {
                    if (!fileEntity.getUserId().equals(userId)) {
                        throw FailCode.NOT_OPERATE_AUTHORITY.getOperateException();
                    }
                }
            }
            // 获取新文件夹下文件
            FileEntity file = new FileEntity();
            file.setFolderId(folderId);
            List<FileEntity> fileEntityList = fileMapper.selectList(new QueryWrapper<>(file));
            for (FileEntity fileEntity : fileEntityList) {
                // 验证文件是否是当前用户的
                if (!fileEntity.getUserId().equals(userId)) {
                    throw FailCode.NOT_OPERATE_AUTHORITY.getOperateException();
                }
                //  验证文件是否重复
                for (FileEntity fileEntity1 : fileList) {
                    if (fileEntity1.getName().equals(fileEntity.getName())) {
                        if (isCopy) {
                            throw FailCode.FILE_EXIST.getOperateException();
                        } else {
                            String name = FileUtils.rename(fileEntity1.getName());
                            fileEntity1.setName(name);
                        }
                    }
                }
            }
            // 创建文件
            for (FileEntity fileEntity : fileList) {
                fileEntity.setId(null);
                fileEntity.setCreateTime(null);
                fileEntity.setUpdateTime(null);
                fileEntity.setVersion(null);
                fileEntity.setFixed(null);
                fileEntity.setFolderId(folderId);
                fileEntity.setUserId(userId);
                if (!retBool(fileMapper.insert(fileEntity))) {
                    throw FailCode.CREATE_FILE_FAIL.getOperateException();
                }
                fileArrayList.add(file);
            }
        }
        return ObjectUtils.convertList(fileArrayList, FileDto.class);
    }

    @Override
    public DownloadModel verifyCdnFile(String referer, String account, String fixed) {
        DownloadModel downloadModel = RedisUtils.get(RedisPrefix.CDN_DOWNLOAD.getKey() + fixed);
        if (downloadModel != null) {
            return downloadModel;
        }
        UserInfoEntity userInfoEntity = new UserInfoEntity();
        userInfoEntity.setAccount(account);
        UserInfoEntity infoEntity = userInfoMapper.selectOne(new QueryWrapper<>(userInfoEntity));
        FileEntity entity = new FileEntity();
        entity.setUserId(infoEntity.getUserId());
        entity.setFixed(fixed);
        FileEntity fileEntity = fileMapper.selectOne(new QueryWrapper<>(entity));
        if (fileEntity == null) {
            throw FailCode.FILE_NOT_EXIST.getOperateException();
        }
        List<FolderEntity> folders = folderService.getParentFolders(fileEntity.getUserId(), fileEntity.getFolderId());
        if (fileEntity.getFolderId().equals(constants.getRootFolder())) {
            FolderEntity folderEntity = new FolderEntity();
            folderEntity.setId(constants.getRootFolder());
            folders.add(folderEntity);
        }
        SafetyChainEntity safetyChainEntity = new SafetyChainEntity();
        safetyChainEntity.setUserId(fileEntity.getUserId());
        List<SafetyChainEntity> safetyChains = safetyChainMapper.selectList(new QueryWrapper<>(safetyChainEntity));
        for (SafetyChainEntity safetyChain : safetyChains) {
            for (FolderEntity folderEntity : folders) {
                boolean bool = constants.getRootFolder().equals(safetyChain.getFolderId()) || folderEntity.getId().equals(safetyChain.getFolderId());
                boolean bool1 = StringUtils.compareUrl(referer, safetyChain.getUrl());
                if (bool || bool1) {
                    downloadModel = ObjectUtils.convert(new DownloadModel(), fileEntity);
                    downloadModel.setIsCompute(true);
                    RedisUtils.set(RedisPrefix.CDN_DOWNLOAD.getKey() + fixed, downloadModel, RedisTime.CDN_FILE.getTime());
                    return downloadModel;
                }
            }
        }
        throw FailCode.NOT_VISIT_AUTHORITY.getOperateException();
    }

    @Override
    public DownloadModel getDownloadModel(String downloadCode, String fid, String downloadType) {
        List<FileDto> dtoList = RedisUtils.get(downloadType + downloadCode);
        if (dtoList == null || dtoList.size() <= 0) {
            throw FailCode.DOWNLOAD_CODE_ERROR.getOperateException();
        }
        // 查看共享文件是否存在
        for (FileDto fileDto : dtoList) {
            if (fileDto.getId().equals(fid)) {
                return ObjectUtils.convert(new DownloadModel(), fileDto);
            }
        }
        throw FailCode.DOWNLOAD_CODE_ERROR.getOperateException();
    }

    @Override
    public void downloadFinish(DownloadModel downloadModel) {
        if (downloadModel.getIsCompute()) {
            userInfoService.computeFreeFlow(downloadModel.getUserId(), downloadModel.getSize(), false);
        }
        if (downloadModel.getShareId() != null) {
            ShareEntity shareEntity = shareMapper.selectById(downloadModel.getShareId());
            shareEntity.setDownloadNum(shareEntity.getDownloadNum() + 1);
            if (!this.retBool(shareMapper.updateById(shareEntity))) {
                throw FailCode.UPDATE_SHARE_FILE_FAIL.getOperateException();
            }
        }
    }

    @Override
    public List<FileDto> setDownloadUrl(List<FileDto> files, String downloadPath) {
        if (files.size() == 0) {
            return new ArrayList<>();
        }
        List<String> hashCodes = new ArrayList<>();
        for (FileDto fileDto : files) {
            hashCodes.add(fileDto.getHashCode());
        }
        //  获取下载地址
        QueryWrapper<FileHashEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("hash_code", hashCodes);
        List<FileHashEntity> fileHashList = fileHashMapper.selectList(queryWrapper);
        List<FileDto> fileDtoList = new ArrayList<>();
        for (FileDto fileDto : files) {
            for (FileHashEntity fileHashEntity : fileHashList) {
                if (fileDto.getHashCode().equals(fileHashEntity.getHashCode())) {
                    //获取后缀
                    String fileName = fileDto.getName();
                    String suffix = "";
                    if (fileName.contains(".")) {
                        int j = fileName.lastIndexOf(".");
                        suffix = fileName.substring(j, fileName.length());
                    }
                    fileDto.setUrl(fileHashEntity.getServerUrl() + downloadPath + fileDto.getId() + suffix);
                    fileDtoList.add(fileDto);
                }
            }
        }
        return fileDtoList;
    }

    @Override
    public void verifyHashCode(List<String> hashCodes, int batchSize, Consumer<List<String>> action) {
        DataUtils.batchHandle(hashCodes, batchSize, (codes) -> {
            QueryWrapper<FileEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.in("hash_code", codes);
            List<FileEntity> fileEntities = fileMapper.selectList(queryWrapper);
            if ((fileEntities == null || fileEntities.size() == 0)) {
                action.accept(codes);
            } else {
                List<String> arrayList = new ArrayList<>();
                for (String hashCode : codes) {
                    boolean bool = false;
                    if (fileEntities.size() != 0) {
                        for (FileEntity fileEntity : fileEntities) {
                            if (fileEntity.getHashCode().equals(hashCode)) {
                                bool = true;
                            }
                        }
                    }
                    if (!bool) {
                        arrayList.add(hashCode);
                    }
                }
                action.accept(arrayList);
            }
        });
    }

    @Override
    public CdnDto createCdnUrl(String userId, CdnBean cdnBean) {
        FileEntity fileEntity = fileMapper.selectById(cdnBean.getFileId());
        if (!fileEntity.getUserId().equals(userId)) {
            throw FailCode.NOT_OPERATE_AUTHORITY.getOperateException();
        }
        fileEntity.setFixed(cdnBean.getFixed());
        if (!this.retBool(fileMapper.updateById(fileEntity))) {
            throw FailCode.UPDATE_FILE_FAIL.getOperateException();
        }
        FileHashEntity fileHashEntity = new FileHashEntity();
        fileHashEntity.setHashCode(fileEntity.getHashCode());
        FileHashEntity hashEntity = fileHashMapper.selectOne(new QueryWrapper<>(fileHashEntity));
        //获取后缀
        String fileName = fileEntity.getName();
        String suffix = "";
        if (fileName.contains(".")) {
            int j = fileName.lastIndexOf(".");
            suffix = fileName.substring(j, fileName.length());
        }
        UserInfoEntity userInfoEntity = new UserInfoEntity();
        userInfoEntity.setUserId(fileEntity.getUserId());
        UserInfoEntity infoEntity = userInfoMapper.selectOne(new QueryWrapper<>(userInfoEntity));
        String url = hashEntity.getServerUrl() + "/cnd_file/" + infoEntity.getAccount() + "/" + fileEntity.getFixed() + suffix;
        CdnDto cdnDto = new CdnDto();
        cdnDto.setCdnUrl(url);
        return cdnDto;
    }

    @Override
    public long getFileSize(List<String> fileIds, String userId) {
        long size = 0;
        if (fileIds.size() > 0) {
            FileEntity file = new FileEntity();
            if (userId != null) {
                file.setUserId(userId);
            }
            QueryWrapper<FileEntity> query = new QueryWrapper<>(file);
            query.in("id", fileIds);
            query.select("sum(size) as total");
            Map<String, Object> map = this.getMap(query);
            if (map != null) {
                size = ((BigDecimal) map.get("total")).longValue();
            }
        }
        return size;
    }

}
